using CxExtension;
using CxExtension.System.Diagnostics;
using Live2D.Cubism.Core;
using Live2D.Cubism.Core.Unmanaged;
using UnityEngine;

namespace Live2D.Cubism.Rendering
{
	public class CubismRendererCombine:MonoBehaviour
	{
		[SerializeField]
		private MeshFilter mMeshFilter;                      //
		[SerializeField]
		private MeshRenderer mMeshRenderer;                      //
		[SerializeField]
		private Mesh mMesh;                      //
		private Mesh mBoundsMesh;                      //
		[SerializeField]
		private Texture2D mMainTexture;                      //
		private bool mBoundsDirty = true; //
		private BoundInfo[] mBoundInfos;//
		[SerializeField]
		private int[] mRaycastTargetIds;//

		public BoundInfo[] BoundInfos
		{
			get
			{
				if (mBoundsDirty)
				{
					UpdateBounds();
				}

				return mBoundInfos;
			}
		}

		private void UpdateBounds()
		{
			ProfilerF.BeginSample("CubismRendererCombine.UpdateBounds");
			mUnmanagedData.GetBounds(mBoundsMesh,mBoundInfos);
			mBoundsDirty = false;
			ProfilerF.EndSample();
		}

		public int[] RaycastTargetIds { get { return mRaycastTargetIds; } }

		public void SetRaycastTargetIds(int[] v)
		{
			mRaycastTargetIds = v;
		}
		public void SetMesh(Mesh v)
		{
			mMesh = v;
		}

		public void SetMeshRenderer(MeshRenderer v)
		{
			mMeshRenderer = v;
		}

		public MeshRenderer MeshRenderer { get { return mMeshRenderer; } }

		public void SetMeshFilter(MeshFilter v)
		{
			mMeshFilter = v;
		}

		private int mSortingOrder;//
		public int SortingOrder { get { return mSortingOrder; } }
		public void SetSortingOrder(int v)
		{
			mSortingOrder = v;
		}


		public Texture2D MainTexture { get { return mMainTexture; } }

		public void SetMainTexture(Texture2D v)
		{
			mMainTexture = v;
			ApplyMainTexture();
		}

		private CubismUnmanagedData mUnmanagedData = new CubismUnmanagedData();

		public CubismUnmanagedData UnmanagedData { get { return mUnmanagedData ; } }

		public string GetPartName(int index)
		{
			return mUnmanagedData.Ids[index];
		}

		/// <summary>
		/// Model the controller belongs to.
		/// </summary>
		private CubismModel Model
		{
			get { return this.FindCubismModel(); }
		}

		/// <summary>
		/// <see cref="SharedPropertyBlock"/> backing field.
		/// </summary>
		private static MaterialPropertyBlock _sharedPropertyBlock;

		/// <summary>
		/// <see cref="MaterialPropertyBlock"/> that can be shared on the main script thread.
		/// </summary>
		private static MaterialPropertyBlock SharedPropertyBlock
		{
			get
			{
				// Lazily initialize.
				if (_sharedPropertyBlock == null)
				{
					_sharedPropertyBlock = new MaterialPropertyBlock();
				}


				return _sharedPropertyBlock;
			}
		}
		/// <summary>
		/// Applies main texture for rendering.
		/// </summary>
		private void ApplyMainTexture()
		{
			mMeshRenderer.GetPropertyBlock(SharedPropertyBlock);
			//mMeshRenderer.sharedMaterial.DisableKeyword("CUBISM_MASK_ON");
			// Write property.
			SharedPropertyBlock.SetTexture(CubismShaderVariables.MainTexture, mMainTexture);
			mMeshRenderer.SetPropertyBlock(SharedPropertyBlock);
		}
		private void OnDynamicDrawableData(CubismModel sender, CubismDynamicDrawableData[] data)
		{
			mBoundsDirty = true;
			ProfilerF.BeginSample("CubismRendererCombine.OnDynamicDrawableData",7);
			mUnmanagedData.CheckChanged();
			ProfilerF.BeginSample("CubismRendererCombine.OnDynamicDrawableData,SetMesh",8);
			if (mUnmanagedData.IsColorDirty)
			{
				ProfilerF.BeginSample("CubismRendererCombine.SetMeshColor",6);
				mMesh.colors = mUnmanagedData.MeshColors;
				ProfilerF.EndSample();
			}
			if (mUnmanagedData.IsVertexDirty)
			{
				ProfilerF.BeginSample("CubismRendererCombine.SetMesh vertices", 6);
				mMesh.vertices = mUnmanagedData.MeshVertices;
				ProfilerF.EndSample();
			}
			if (mUnmanagedData.IsRenderOrderDirty)
			{
				ProfilerF.BeginSample("CubismRendererCombine.SetMesh triangles", 6);
				//mMesh.triangles = mUnmanagedData.MeshTriangles;
				ProfilerF.EndSample();
				ProfilerF.BeginSample("CubismRendererCombine.SetMesh trianglesE", 6);
				mMesh.SetTrianglesE(mUnmanagedData.MeshTriangles, mUnmanagedData.MeshTrianglesCount,true);
				ProfilerF.EndSample();
			}
			mUnmanagedData.ClearFlags();
			ProfilerF.EndSample();
			ProfilerF.EndSample();
		}

		#region UnityMothod

		void Awake()
		{
			if (mMesh == null)
			{
				mMesh = new Mesh();
				mMesh.MarkDynamic();
				mMeshFilter.mesh = mMesh;

				mBoundsMesh = new Mesh();
				mBoundsMesh.MarkDynamic();
				mBoundInfos = new BoundInfo[mRaycastTargetIds.Length];
				for (int i = 0, length = mRaycastTargetIds.Length; i < length; i++)
				{
					var it = mRaycastTargetIds[i];
					var bound = new BoundInfo {partIndex = it,dirty = true};
					mBoundInfos[i] = bound;
				}

				mBoundsDirty = true;
			}
		}
		void OnEnable()
		{
			// Fail silently.
			if (Model == null)
			{
				return;
			}


			// Make sure renderers are available.
			TryInitializeRenderers();

			Model.OnDynamicDrawableData += OnDynamicDrawableData;
		}

		private void TryInitializeRenderers()
		{

			// Register listener.
#if UNITY_LIVE2D_COMBINE_MESH
			var drawablesNode = transform.Find("Drawables");
			if (drawablesNode != null)
			{
				drawablesNode.gameObject.SetActive(false);
			}
			var model = Model;
			if (mUnmanagedData.Inited)
			{
				return;
			}
			mUnmanagedData.InitByModel(model.TaskableModel.UnmanagedModel.Ptr);

			mMesh.vertices = mUnmanagedData.MeshVertices;
			mMesh.uv = mUnmanagedData.MeshUv;
			//mMesh.triangles = mUnmanagedData.MeshTriangles;
			mMesh.SetTrianglesE(mUnmanagedData.MeshTriangles,mUnmanagedData.MeshTrianglesCount,true);
			mMesh.colors = mUnmanagedData.MeshColors;
			//mMesh.RecalculateBounds();
			ApplyMainTexture();
#else
#endif
		}


		void OnDisable()
		{
			// Fail silently.
			if (Model == null)
			{
				return;
			}


			// Deregister listener.
			Model.OnDynamicDrawableData -= OnDynamicDrawableData;
		}
		#endregion

	}
}